/**
 * BC Math Library for Javascript
 * Ported from the PHP5 bcmath extension source code,
 * which uses the libbcmath package...
 *    Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
 *    Copyright (C) 2000 Philip A. Nelson
 *     The Free Software Foundation, Inc.
 *     59 Temple Place, Suite 330
 *     Boston, MA 02111-1307 USA.
 *      e-mail:  philnelson@acm.org
 *     us-mail:  Philip A. Nelson
 *               Computer Science Department, 9062
 *               Western Washington University
 *               Bellingham, WA 98226-9062
 *
 * bcmath-js homepage:
 *
 * This code is covered under the LGPL licence, and can be used however you want :)
 * Be kind and share any decent code changes.
 */

var libbcmath = {
  PLUS: '+',
  MINUS: '-',
  BASE: 10,           // must be 10 (for now)
  scale: 0,           // default scale

  /**
     * Basic number structure
     */
  bc_num: function() {
    this.n_sign = null; // sign
    this.n_len = null;  /* (int) The number of digits before the decimal point. */
    this.n_scale = null; /* (int) The number of digits after the decimal point. */
    //this.n_refs = null; /* (int) The number of pointers to this number. */
    //this.n_text = null; /* ?? Linked list for available list. */
    this.n_value = null;  /* array as value, where 1.23 = [1,2,3] */
    this.toString = function() {
      var r, tmp;
      tmp = this.n_value.join('');

      // add minus sign (if applicable) then add the integer part
      r = ((this.n_sign == libbcmath.PLUS) ? '' : this.n_sign) + tmp.substr(0, this.n_len);

      // if decimal places, add a . and the decimal part
      if (this.n_scale > 0) {
        r += '.' + tmp.substr(this.n_len, this.n_scale);
      }
      return r;
    };
  },

  /**
     * @param int length
     * @param int scale
     * @return bc_num
     */
  bc_new_num: function(length, scale) {
    var temp; // bc_num
    temp = new libbcmath.bc_num();
    temp.n_sign = libbcmath.PLUS;
    temp.n_len = length;
    temp.n_scale = scale;
    temp.n_value = libbcmath.safe_emalloc(1, length + scale, 0);
    libbcmath.memset(temp.n_value, 0, 0, length + scale);
    return temp;
  },

  safe_emalloc: function(size, len, extra) {
    return Array((size * len) + extra);
  },

  /**
     * Create a new number
     */
  bc_init_num: function() {
    return new libbcmath.bc_new_num(1, 0);

  },

  _bc_rm_leading_zeros: function(num) {
    /* We can move n_value to point to the first non zero digit! */
    while ((num.n_value[0] === 0) && (num.n_len > 1)) {
      num.n_value.shift();
      num.n_len--;
    }
  },

  /**
     * Convert to bc_num detecting scale
     */
  php_str2num: function(str) {
    var p;
    p = str.indexOf('.');
    if (p == -1) {
      return libbcmath.bc_str2num(str, 0);
    } else {
      return libbcmath.bc_str2num(str, (str.length - p));
    }

  },

  CH_VAL: function(c) {
    return c - '0'; //??
  },

  BCD_CHAR: function(d) {
    return d + '0'; // ??
  },

  isdigit: function(c) {
    return (isNaN(parseInt(c, 10)) ? false : true);
  },

  bc_str2num: function(str_in, scale) {
    var str, num, ptr, digits, strscale, zero_int, nptr;
    // remove any non-expected characters
    /* Check for valid number and count digits. */

    str = str_in.split(''); // convert to array
    ptr = 0;    // str
    digits = 0;
    strscale = 0;
    zero_int = false;
    if ((str[ptr] === '+') || (str[ptr] === '-')) {
      ptr++;  /* Sign */
    }
    while (str[ptr] === '0') {
      ptr++;            /* Skip leading zeros. */
    }
    //while (libbcmath.isdigit(str[ptr])) {
    while ((str[ptr]) % 1 === 0) { //libbcmath.isdigit(str[ptr])) {
      ptr++;
      digits++;    /* digits */
    }

    if (str[ptr] === '.') {
      ptr++;            /* decimal point */
    }
    //while (libbcmath.isdigit(str[ptr])) {
    while ((str[ptr]) % 1 === 0) { //libbcmath.isdigit(str[ptr])) {
      ptr++;
      strscale++;    /* digits */
    }

    if ((str[ptr]) || (digits + strscale === 0)) {
      // invalid number, return 0
      return libbcmath.bc_init_num();
      //*num = bc_copy_num (BCG(_zero_));
    }

    /* Adjust numbers and allocate storage and initialize fields. */
    strscale = libbcmath.MIN(strscale, scale);
    if (digits === 0) {
      zero_int = true;
      digits = 1;
    }

    num = libbcmath.bc_new_num(digits, strscale);

    /* Build the whole number. */
    ptr = 0; // str
    if (str[ptr] === '-') {
      num.n_sign = libbcmath.MINUS;
      //(*num)->n_sign = MINUS;
      ptr++;
    } else {
      num.n_sign = libbcmath.PLUS;
      //(*num)->n_sign = PLUS;
      if (str[ptr] === '+') {
        ptr++;
      }
    }
    while (str[ptr] === '0') {
      ptr++;            /* Skip leading zeros. */
    }

    nptr = 0; //(*num)->n_value;
    if (zero_int) {
      num.n_value[nptr++] = 0;
      digits = 0;
    }
    for (; digits > 0; digits--) {
      num.n_value[nptr++] = libbcmath.CH_VAL(str[ptr++]);
      //*nptr++ = CH_VAL(*ptr++);
    }

    /* Build the fractional part. */
    if (strscale > 0) {
      ptr++;  /* skip the decimal point! */
      for (; strscale > 0; strscale--) {
        num.n_value[nptr++] = libbcmath.CH_VAL(str[ptr++]);
      }
    }

    return num;
  },

  cint: function(v) {
    if (typeof(v) == 'undefined') {
      v = 0;
    }
    var x = parseInt(v, 10);
    if (isNaN(x)) {
      x = 0;
    }
    return x;
  },

  /**
     * Basic min function
     * @param int
     * @param int
     */
  MIN: function(a, b) {
    return ((a > b) ? b : a);
  },

  /**
     * Basic max function
     * @param int
     * @param int
     */
  MAX: function(a, b) {
    return ((a > b) ? a : b);
  },

  /**
     * Basic odd function
     * @param int
     * @param int
     */
  ODD: function(a) {
    return (a & 1);
  },

  /**
     * replicate c function
     * @param array     return (by reference)
     * @param string    char to fill
     * @param int       length to fill
     */
  memset: function(r, ptr, chr, len) {
    var i;
    for (i = 0; i < len; i++) {
      r[ptr + i] = chr;
    }
  },

  /**
     * Replacement c function
     * Obviously can't work like c does, so we've added an "offset" param so you could do memcpy(dest+1, src, len) as memcpy(dest, 1, src, len)
     * Also only works on arrays
     */
  memcpy: function(dest, ptr, src, srcptr, len) {
    var i;
    for (i = 0; i < len; i++) {
      dest[ptr + i] = src[srcptr + i];
    }
    return true;
  },


  /**
     * Determine if the number specified is zero or not
     * @param bc_num num    number to check
     * @return boolean      true when zero, false when not zero.
     */
  bc_is_zero: function(num) {
    var count; // int
    var nptr; // int

    /* Quick check. */
    //if (num == BCG(_zero_)) return TRUE;

    /* Initialize */
    count = num.n_len + num.n_scale;
    nptr = 0; //num->n_value;

    /* The check */
    while ((count > 0) && (num.n_value[nptr++] === 0)) {
      count--;
    }

    if (count !== 0) {
      return false;
    } else {
      return true;
    }
  },

  bc_out_of_memory: function() {
    throw new Error('(BC) Out of memory');
  }
};

